---
title: Calendar
description: A component that allows users to select a single date.
links:
  - label: Aria docs
    href: https://react-spectrum.adobe.com/react-aria/Calendar.html
  - label: "@internationalized/date"
    href: https://react-spectrum.adobe.com/internationalized/date/index.html
  - label: Report an issue
    href: https://github.com/mehdibha/dotUI/issues/new/choose
  - label: Edit this page
    href: https://github.com/mehdibha/dotUI/tree/main/content/components/date-and-time/calendar.mdx?plain=1
---

<ComponentPreview
  name="calendar/demos/calendar/default"
  preview={`<Calendar aria-label="Appointment date" />`}
/>

## Installation

```package-install
npx shadcn@latest add @dotui/calendar
```

## Usage

Use `Calendar` to allow users to select a single date.

```tsx
import { parseDate } from "@internationalized/date";

import { Calendar } from "@/components/core/calendar";

<Calendar
  aria-label="Appointment date"
  defaultValue={parseDate("2025-01-01")}
/>;
```

```tsx
import { parseDate } from "@internationalized/date";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";

import { Button } from "@/components/core/button";
import {
  CalendarCell,
  CalendarGrid,
  CalendarGridBody,
  CalendarGridHeader,
  CalendarHeader,
  CalendarHeaderCell,
  CalendarRoot,
} from "@/components/core/calendar";
import { Heading } from "@/registry/ui/default/core/heading";

<CalendarRoot
  aria-label="Appointment date"
  defaultValue={parseDate("2025-01-01")}
>
  <CalendarHeader>
    <Button slot="previous">
      <ChevronLeftIcon />
    </Button>
    <Heading />
    <Button slot="next">
      <ChevronRightIcon />
    </Button>
  </CalendarHeader>
  <CalendarGrid>
    <CalendarGridHeader>
      {(day) => <CalendarHeaderCell>{day}</CalendarHeaderCell>}
    </CalendarGridHeader>
    <CalendarGridBody>
      {(date) => <CalendarCell date={date} />}
    </CalendarGridBody>
  </CalendarGrid>
</CalendarRoot>;
```

### Best practices

- An `aria-label` must be provided to the `Calendar` for accessibility. If it is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead.

## Uncontrolled

An initial, uncontrolled value can be provided to the `Calendar` using the `defaultValue` prop.

<ComponentPreview
  name="calendar/demos/calendar/uncontrolled"
  preview={`<Calendar aria-label="Appointment date" defaultValue={parseDate("2025-01-01")} />`}
/>

## Controlled

The `Calendar` component can be controlled by passing the `value` and `onChange` props.

<ComponentPreview
  name="calendar/demos/calendar/controlled"
  preview={`const [value, setValue] = React.useState<DateValue>(parseDate('2025-01-01'))
return <Calendar aria-label="Event date" value={value} onChange={setValue} />`}
/>

## Validation

### Min and max values

By default, `Calendar` allows selecting any date. The `minValue` and `maxValue` props can also be used to prevent the user from selecting dates outside a certain range.

This example only accepts dates after today.

<ComponentPreview
  name="calendar/demos/calendar/validation"
  preview={`<Calendar aria-label="Appointment date" minValue={today(getLocalTimeZone())} />`}
/>

### Unavailable dates

`Calendar` supports marking certain dates as unavailable. These dates cannot be selected by the user and are displayed with a crossed out appearance. The `isDateUnavailable` prop accepts a callback that is called to evaluate whether each visible date is unavailable.

This example includes multiple unavailable date ranges, e.g. dates when no appointments are available. In addition, all weekends are unavailable. The minValue prop is also used to prevent selecting dates before today.

{/* prettier-ignore-start */}

<ComponentPreview 
  name="calendar/demos/calendar/unvailable-dates" 
  preview={`const now = today(getLocalTimeZone());
  const disabledRanges = [
    [now, now.add({ days: 5 })],
    [now.add({ days: 14 }), now.add({ days: 16 })],
    [now.add({ days: 23 }), now.add({ days: 24 })],
  ];

  const { locale } = useLocale();
  const isDateUnavailable = (date: DateValue) =>
    isWeekend(date, locale) ||
    disabledRanges.some(
      (interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0
    );

  return (
    <Calendar
      minValue={today(getLocalTimeZone())}
      isDateUnavailable={isDateUnavailable}
    />
);`}
/>

{/* prettier-ignore-end */}

### Error message

`Calendar` tries to avoid allowing the user to select invalid dates in the first place (see [Min and max values](/docs/components/date-and-time/calendar#min-and-max-values) and [Unavailable dates](/docs/components/date-and-time/calendar#unavailable-dates). However, if according to application logic a selected date is invalid, Use `isInvalid` and `errorMessage` props.

This example validates that the selected date is a weekday and not a weekend according to the current locale.

<ComponentPreview
  name="calendar/demos/calendar/error-message"
  preview={`const [date, setDate] = React.useState(today(getLocalTimeZone()));
  const { locale } = useLocale();
  const isInvalid = isWeekend(date, locale);
  return (
    <Calendar
      aria-label="Appointment date"
      value={date}
      onChange={setDate}
      isInvalid={isInvalid}
      errorMessage={"We are closed on weekends"}
    />

);`}
/>

## Options

### Variant

The `variant` prop can be used to change the appearance of the `Calendar`.

<ComponentPreview
  name="calendar/demos/calendar/variant"
  preview={`<Calendar
    aria-label="Appointment date"
    variant="primary"
    defaultValue={parseDate("2025-01-01")}
  />`}
/>

### Visible months

By default, `Calendar` displays a single month. The `visibleMonths` prop allows displaying up to 3 months at a time.

<ComponentPreview
  name="calendar/demos/calendar/visible-months"
  preview={`<Calendar aria-label="Appointment date" visibleMonths={2} />`}
/>

### Page behaviour

The `pageBehavior` prop allows you to control how the calendar navigates between months.

<ComponentPreview
  name="calendar/demos/calendar/page-behaviour"
  preview={`<Calendar aria-label="Appointment date" visibleMonths={2} pageBehavior="single" />`}
/>

### Disabled

The `isDisabled` boolean prop makes the Calendar disabled. Cells cannot be focused or selected.

<ComponentPreview
  name="calendar/demos/calendar/disabled"
  preview={`<Calendar aria-label="Appointment date" isDisabled />`}
/>

### Read only

The `isReadOnly` boolean prop makes the Calendar's value immutable. Unlike `isDisabled`, the Calendar remains focusable.

<ComponentPreview
  name="calendar/demos/calendar/read-only"
  preview={`<Calendar aria-label="Appointment date" isReadOnly value={today(getLocalTimeZone())} />`}
/>

## Composition

If you need to customize things further, you can drop down to the composition level.

<ComponentPreview
  suspense
  name="calendar/demos/calendar/composition"
  preview={`<CalendarRoot aria-label="Event date">
    <CalendarHeader>
      <Button slot="previous">
        <ChevronLeftIcon />
      </Button>
      <Heading />
      <Button slot="next">
        <ChevronRightIcon />
      </Button>
    </CalendarHeader>
    <CalendarGrid>
      <CalendarGridHeader>
        {(day) => <CalendarHeaderCell>{day}</CalendarHeaderCell>}
      </CalendarGridHeader>
      <CalendarGridBody>
        {(date) => <CalendarCell date={date} />}
      </CalendarGridBody>
    </CalendarGrid>
  </CalendarRoot>`}
/>

## API Reference

| Prop                  | Type                                                                                                  | Default     | Description                                                                                                                                         |
| --------------------- | ----------------------------------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `visibleMonths`       | `number`                                                                                              | `1`         | The number of months to display at once. Up to 3 months are supported.                                                                              |
| `minValue`            | `DateValue`                                                                                           | -           | The minimum allowed date that a user may select.                                                                                                    |
| `maxValue`            | `DateValue`                                                                                           | -           | The maximum allowed date that a user may select.                                                                                                    |
| `isDateUnavailable`   | `(date: DateValue) => boolean`                                                                        | -           | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable.                                            |
| `isDisabled`          | `boolean`                                                                                             | `false`     | Whether the calendar is disabled.                                                                                                                   |
| `isReadOnly`          | `boolean`                                                                                             | `false`     | Whether the calendar value is immutable.                                                                                                            |
| `autoFocus`           | `boolean`                                                                                             | `false`     | Whether to automatically focus the calendar when it mounts.                                                                                         |
| `focusedValue`        | `DateValue`                                                                                           | -           | Controls the currently focused date within the calendar.                                                                                            |
| `defaultFocusedValue` | `DateValue`                                                                                           | -           | The date that is focused when the calendar first mounts (uncountrolled).                                                                            |
| `isInvalid`           | `boolean`                                                                                             | -           | Whether the current selection is invalid according to application logic.                                                                            |
| `pageBehavior`        | `'single' \| 'visible'`                                                                               | `'visible'` | Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration. |
| `value`               | `DateValue \| null`                                                                                   | -           | The current value (controlled).                                                                                                                     |
| `defaultValue`        | `DateValue \| null`                                                                                   | -           | The default value (uncontrolled).                                                                                                                   |
| `children`            | `ReactNode \| (values: CalendarRenderProps & {defaultChildren: ReactNode \| undefined}) => ReactNode` | -           | The children of the component. A function may be provided to alter the children based on component state.                                           |
| `className`           | `string`                                                                                              | -           | The CSS className for the element.                                                                                                                  |
| `style`               | `CSSProperties \| (values: CalendarRenderProps & {defaultStyle: CSSProperties}) => CSSProperties`     | -           | The inline style for the element. A function may be provided to compute the style based on component state.                                         |

| Event           | Type                                          | Description                                           |
| --------------- | --------------------------------------------- | ----------------------------------------------------- |
| `onFocusChange` | `(date: CalendarDate) => void`                | Handler that is called when the focused date changes. |
| `onChange`      | `(value: MappedDateValue<DateValue>) => void` | Handler that is called when the value changes.        |

## Accessibility

### Keyboard interactions

| Key             | Description                                                   |
| --------------- | ------------------------------------------------------------- |
| `Tab`           | Moves focus to the next focusable item in the calendar.       |
| `Shift+Tab`     | Moves focus to the previous focusable item in the calendar.   |
| `ArrowRight`    | Moves focus to the next day.                                  |
| `ArrowLeft`     | Moves focus to the previous day.                              |
| `ArrowDown`     | Moves focus to the same day of the week in the next week.     |
| `ArrowUp`       | Moves focus to the same day of the week in the previous week. |
| `Space` `Enter` | Selects the focused date.                                     |
